import 'package:family_expense/features/income_expense/constants/income_expense_type.dart';
import 'package:family_expense/features/income_expense_detail_type/models/income_expense_detail_type.dart';
import 'package:family_expense/features/income_expense_detail_type/services/income_expense_detail_type_loader.dart';
import 'package:family_expense/features/income_expense_detail_type/services/income_expense_detail_type_service.dart';
import 'package:family_expense/features/income_expense_detail_type/widgets/edit_income_expense_detail_type.dart';
import 'package:family_expense/features/income_expense_detail_type/widgets/income_expense_detail_type_info.dart';
import 'package:family_expense/shared/services/system_setting_service.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

class IncomeExpenseDetailTypeHome extends StatefulWidget {
  const IncomeExpenseDetailTypeHome({super.key});

  @override
  State<IncomeExpenseDetailTypeHome> createState() =>
      _IncomeExpenseDetailTypeHomeState();
}

class _IncomeExpenseDetailTypeHomeState
    extends State<IncomeExpenseDetailTypeHome> {
  // Stores all selected [IncomeExpenseDetailType] for actions.
  final List<IncomeExpenseDetailType> _selectedDetailTypes = [];

  // For [showBottomSheet].
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  // Resets data list order to server and batch deleltes items.
  final IncomeExpenseDetailTypeService _detailTypeService =
      IncomeExpenseDetailTypeService();

  // Indicates how many items selected. It's used in bottom sheet.
  int _selectedCount = 0;

  // Indicates if display the [CheckListTile] for selection.
  bool _enableItemSelection = false;

  // Indicates if any data changed. Including adding, updating and deleting.
  bool _dataUpdated = false;

  // Current selected income/expense type.
  String _incomeExpenseType = IncomeExpenseType.expense;

  // For caculating PopupMenuButton text color.
  Color _popupMenuButtonColor = Colors.black;

  // The loaded detail types for list view.
  List<IncomeExpenseDetailType>? _detailTypes;

  // Selected item deleting menu.
  PersistentBottomSheetController? _bottomSheetController;

  // Current authenticated user.
  User? _user;

  @override
  void initState() {
    super.initState();

    _getSetting();
    _getCurrentUser();

    // First load data for the list.
    _loadData(init: true);
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false,
      onPopInvokedWithResult: _onPopInvokedWithResult,
      child: Scaffold(
        key: _scaffoldKey,
        appBar: _buildAppBar(),
        body: _user?.isAccountGroupAdmin == true
            ? _buildReorderableBody()
            : _buildBody(),
      ),
    );
  }

  // Builds the app bar and actions.
  PreferredSizeWidget _buildAppBar() {
    return AppBar(
      title: _user?.isAccountGroupAdmin == true
          ? Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  _enableItemSelection
                      ? '已选择 $_selectedCount 个${IncomeExpenseType.getCategories()[_incomeExpenseType]}类别'
                      : '${IncomeExpenseType.getCategories()[_incomeExpenseType]}类别管理',
                ),
                const Text('长按可拖动改变顺序', style: MyFontStyle.appBarSubtitleStyle),
              ],
            )
          : Text('${IncomeExpenseType.getCategories()[_incomeExpenseType]}类别'),
      actions: <Widget>[
        if (_user?.isAccountGroupAdmin == true)
          IconButton(
            icon: _enableItemSelection
                ? Icon(MdiIcons.cancel)
                : Icon(MdiIcons.checkboxMarkedOutline),
            tooltip: _enableItemSelection ? '退出选择模式' : '进入选择模式',
            onPressed: _toggleItemSelection,
          ),
        if (_user?.isAccountGroupAdmin == true)
          IconButton(
            icon: const Icon(Icons.add),
            tooltip: '添加类别',
            onPressed: _gotoEditView,
          ),
        PopupMenuButton<String>(
          icon: Text(
            IncomeExpenseType.getCategories()[_incomeExpenseType]!,
            style: TextStyle(color: _popupMenuButtonColor),
          ),
          onSelected: (type) {
            _incomeExpenseType = type;
            _loadData();
          },
          itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
            PopupMenuItem<String>(
              value: IncomeExpenseType.expense,
              child: _incomeExpenseType == IncomeExpenseType.expense
                  ? Text(
                      IncomeExpenseType.getCategories()[IncomeExpenseType
                          .expense]!,
                      style: const TextStyle(color: Colors.blue),
                    )
                  : Text(
                      IncomeExpenseType.getCategories()[IncomeExpenseType
                          .expense]!,
                    ),
            ),
            PopupMenuItem<String>(
              value: IncomeExpenseType.income,
              child: _incomeExpenseType == IncomeExpenseType.income
                  ? Text(
                      IncomeExpenseType.getCategories()[IncomeExpenseType
                          .income]!,
                      style: const TextStyle(color: Colors.blue),
                    )
                  : Text(
                      IncomeExpenseType.getCategories()[IncomeExpenseType
                          .income]!,
                    ),
            ),
          ],
        ),
      ],
    );
  }

  // Builds the list when data loading is done. Otherwise the loading indicator will be returned.
  Widget _buildBody() {
    if (_detailTypes == null) {
      return const MyPageTopCenterLoadingIndicator();
    }

    return ListView(
      children: _detailTypes!.map((e) => _bulidListTile(e)).toList(),
    );
  }

  // Builds the draggable list when data loading is done. Otherwise the loading indicator will be returned.
  Widget _buildReorderableBody() {
    if (_detailTypes == null) {
      return const MyPageTopCenterLoadingIndicator();
    }

    // ReorderableListView is appropriate for views with a small number of children because constructing the List requires doing
    // work for every child that could possibly be displayed in the list view instead of just those children that are actually visible.
    // All list items must have a key.
    return ReorderableListView(
      onReorder: _onReorder,
      children: _detailTypes!
          .map(
            (e) => _enableItemSelection
                ? _bulidCheckboxListTile(e)
                : _bulidListTile(e),
          )
          .toList(),
    );
  }

  /// Builds the list tile with checkbox for each detail type selection.
  Widget _bulidCheckboxListTile(IncomeExpenseDetailType detailType) {
    return CheckboxListTile(
      key: Key(detailType.id!),
      title: Text(detailType.title!),
      subtitle: detailType.subtitle.isNotNullOrEmpty
          ? Text(detailType.subtitle!)
          : null,
      value: _checkIsSelected(detailType),
      selected: _checkIsSelected(detailType),
      selectedTileColor: Colors.red[50],
      onChanged: (value) => _onCheckListTileChange(value, detailType),
    );
  }

  /// Gets current system setting.
  Future<void> _getSetting() async {
    final res = await SystemSettingService.setting;

    if (mounted) {
      setState(
        () => _popupMenuButtonColor = calculateContrastingColor(
          res.themeSetting!.primaryColor,
        ),
      );
    }
  }

  /// Gets current authenticated user.
  Future<void> _getCurrentUser() async {
    final user = await AuthStateService.instance.currentUser;

    if (mounted) {
      setState(() => _user = user);
    }
  }

  // Changes the item order when the dragging completes.
  void _onReorder(int oldIndex, int newIndex) {
    // Changes the list with updated item order index.
    setState(() {
      // Why we need to re-calcuate [newIndex]?
      // Official doc tells me to do it.
      // https://api.flutter.dev/flutter/material/ReorderableListView-class.html
      if (oldIndex < newIndex) {
        newIndex -= 1;
      }

      final row = _detailTypes!.removeAt(oldIndex);
      _detailTypes!.insert(newIndex, row);
    });

    // Gets re-ordered detail type ids.
    final detailTypeIds = List<String>.from(
      _detailTypes!.map((detailType) => detailType.id),
    );

    // Resets the order via service.
    _detailTypeService.resetOrder(detailTypeIds);

    // Updates the cache data.
    IncomeExpenseDetailTypeLoader.reset(_incomeExpenseType, _detailTypes!);

    // If [_dataUpdated] has NOT been set and any data has been changed then we need to mark it.
    if (!_dataUpdated) {
      _dataUpdated = true;
    }
  }

  /// Starts item selection if [_enableItemSelection] is true. Or completes item selection if [_enableItemSelection] is true.
  ///
  /// It will change the value of [_enableItemSelection] and show or close the deletion action.
  void _toggleItemSelection() {
    if (mounted) {
      setState(() => _enableItemSelection = !_enableItemSelection);
    }

    if (!_enableItemSelection) {
      _bottomSheetController!.close();
    } else {
      _bottomSheetController = _scaffoldKey.currentState!.showBottomSheet(
        (context) => getSelectionBottomSheetContent([
          IconButtonWithBottomLabel(
            icon: const Icon(Icons.delete),
            label: const Text('删除'),
            onPressed: _selectedCount > 0 ? _deleteDetailType : null,
          ),
        ]),
      );

      // Never put it in the [build] method.
      // Otherwise the inner [setState] will trigger [builder] forever.
      // BottomSheet might be closed by pressing back button.
      // That's why we need to use [closed] future.
      // [whenComplete] is the asynchronous equivalent of a "finally" block.
      _bottomSheetController!.closed.whenComplete(() {
        if (mounted) {
          setState(() {
            // Resets the button text.
            _enableItemSelection = false;

            // Resets the item selections.
            _selectedDetailTypes.clear();
          });
        }

        // Updates the delete button status in bottom sheet.
        _bottomSheetController!.setState!(() {
          _selectedCount = 0;
        });
      });
    }
  }

  /// Checks if the provided [detailType] is selected.
  bool _checkIsSelected(IncomeExpenseDetailType detailType) {
    return _selectedDetailTypes.contains(detailType);
  }

  /// Builds the list tile for detail type list view.
  Widget _bulidListTile(IncomeExpenseDetailType detailType) {
    return ListTile(
      key: Key(detailType.id!),
      title: Text(detailType.title!),
      subtitle: detailType.subtitle.isNotNullOrEmpty
          ? Text(detailType.subtitle!)
          : null,
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (!detailType.analysisIncluded!)
            const Chip(
              padding: EdgeInsets.all(0.0),
              label: Text('不包含'),
              backgroundColor: Colors.orangeAccent,
            ),
          const ListTileNavigateNextIcon(),
        ],
      ),
      onTap: () => _gotoDetailView(detailType),
    );
  }

  /// Adds or removes [detailType] to/from [_selectedDetailTypes] based on the value of [checked].
  void _onCheckListTileChange(
    bool? checked,
    IncomeExpenseDetailType detailType,
  ) {
    if (mounted) {
      // If the [checked] is true means current [detailType] is selected. We add it to [_selectedDetailTypes].
      // If the [checked] is false means current [detailType] is deselected. We remove it from [_selectedDetailTypes].
      // [setState] triggers build.
      setState(
        () => checked == true
            ? _selectedDetailTypes.add(detailType)
            : _selectedDetailTypes.remove(detailType),
      );
    }

    // Updates the delete button in bottom sheet.
    _bottomSheetController!.setState!(
      () => _selectedCount = _selectedDetailTypes.length,
    );
  }

  /// Loads the data once [_incomeExpenseType] changes.
  Future<void> _loadData({bool init = false}) async {
    if (mounted) {
      // Resets [_detailTypes] to null for displaying the loading indicator while retrieving data.
      setState(() {
        _detailTypes = null;
        _selectedDetailTypes.clear();

        // Updates the delete button status in bottom sheet.
        _bottomSheetController?.setState!(() {
          _selectedCount = 0;
        });
      });
    }

    List<IncomeExpenseDetailType> detailTypes = [];

    if (init) {
      // Re-init just in case other users make updates.
      IncomeExpenseDetailTypeLoader.clearCache();
      await IncomeExpenseDetailTypeLoader.init();

      // Retrieves data with income/expense type from cache.
      // Why we are using the reference list because we are changing the item in the list.
      // Including adding, updating, deleting and changing order.
      // Using reference type makes it easy and no need to do any sync action.
      detailTypes = IncomeExpenseDetailTypeLoader.getDetailTypes(
        _incomeExpenseType,
      );
    } else {
      // Queries data from api.
      // Why we are using the reference list because we are changing the item in the list.
      // Including adding, updating, deleting and changing order.
      // Using reference type makes it easy and no need to do any sync action.
      detailTypes = await _detailTypeService.queryByAccountGroup(
        incomeExpenseType: _incomeExpenseType,
      );

      // Updates cache data.
      IncomeExpenseDetailTypeLoader.reset(_incomeExpenseType, detailTypes);
    }

    if (mounted) {
      setState(() => _detailTypes = detailTypes);
    }
  }

  /// Goes to the edit view.
  Future<void> _gotoEditView() async {
    // The updated data passed from [EditIncomeExpenseDetailType] and will be inserted/updated to the list.
    final updatedDetailType =
        await NavigatorHelper.push<IncomeExpenseDetailType>(
          context,
          EditIncomeExpenseDetailType(
            IncomeExpenseDetailType(incomeExpenseType: _incomeExpenseType),
          ),
        );

    if (updatedDetailType != null) {
      // Adds new detail type to cache.
      IncomeExpenseDetailTypeLoader.change(
        _incomeExpenseType,
        updatedDetailType.clone(),
      );

      _loadData();

      // If [_dataUpdated] has NOT been set and any data has been changed then we need to mark it.
      if (!_dataUpdated) {
        _dataUpdated = true;
      }
    }
  }

  // Goes to detail view and reloads data if data updated.
  Future<void> _gotoDetailView(IncomeExpenseDetailType detailType) async {
    // The updated data passed from [EditIncomeExpenseDetailType] and will be inserted/updated to the list.
    final result = await NavigatorHelper.push<dynamic>(
      context,
      IncomeExpenseDetailTypeInfo(detailType),
    );

    // If result is instance of [IncomeExpenseDetailType] that means it has been updated.
    // If result is [String] that means it has been deleted.
    if (result != null) {
      if (result is IncomeExpenseDetailType) {
        // Gets the index in the list with id.
        final index = _detailTypes!.indexWhere((p) => p.id == result.id);

        IncomeExpenseDetailTypeLoader.change(
          _incomeExpenseType,
          result.clone(),
          index: index,
        );
      } else if (result is String) {
        IncomeExpenseDetailTypeLoader.change(
          _incomeExpenseType,
          IncomeExpenseDetailType(id: result),
        );
      }

      _loadData();

      // If [_dataUpdated] has NOT been set and any data has been changed then we need to mark it.
      if (!_dataUpdated) {
        _dataUpdated = true;
      }
    }
  }

  /// Deletes all selected detail types. All used detail types cannot be deleted.
  void _deleteDetailType() {
    showConfirmDialog(
      context,
      '删除这 $_selectedCount 个${IncomeExpenseType.getCategories()[_incomeExpenseType]}类别吗？',
      onConfirm: () async {
        showInProgressOverlay(context);

        // Deletes item on back server.
        final deletedDetailTypeIds = await _detailTypeService.batchDelete(
          _incomeExpenseType,
          List<String>.from(_selectedDetailTypes.map((p) => p.name)),
        );

        // DO NOT use BuildContext across asynchronous gaps.
        // https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html
        if (mounted) {
          // Closes progress overlay.
          NavigatorHelper.pop(context);
        }

        if (deletedDetailTypeIds.length < _selectedCount) {
          showToast(
            '${_selectedCount - deletedDetailTypeIds.length} 个已被使用的类别未能删除',
          );
        }

        // Removes deleted detail types from list.
        for (final p in deletedDetailTypeIds) {
          IncomeExpenseDetailTypeLoader.change(
            _incomeExpenseType,
            IncomeExpenseDetailType(id: p),
          );
        }

        _loadData();

        // If [_dataUpdated] has NOT been set and any data has been deleted then we need to mark it.
        if (!_dataUpdated && deletedDetailTypeIds.isNotEmpty) {
          _dataUpdated = true;
        }

        // Restores selection mode.
        _toggleItemSelection();
      },
    );
  }

  /// Checks the data before popping.
  void _onPopInvokedWithResult(bool didPop, dynamic result) {
    if (didPop) {
      return;
    }

    if (_enableItemSelection) {
      _toggleItemSelection();
    } else {
      NavigatorHelper.pop<bool>(context, _dataUpdated);
    }
  }
}
